import logging
from colorlog import ColoredFormatter
import os
from pathlib import Path
import shutil
import operator
from loguru import logger


def get_list_name(obj):
    if type(obj) is list:
        for i in range(len(obj)):
            if callable(obj[i]):
                obj[i] = obj[i].__name__
    elif callable(obj):
        obj = obj.__name__
    return obj


class LogUtil:

    _instance = None

    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

    def __init__(self) -> None:
        self.logger = LogUtil.setup_logger()

    @staticmethod
    def setup_logger():
        """Return a logger with a default ColoredFormatter."""
        formatter = ColoredFormatter(
            "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s",
            datefmt=None,
            reset=True,
            log_colors={
                "DEBUG": "cyan",
                "INFO": "green",
                "WARNING": "yellow",
                "ERROR": "red",
                "CRITICAL": "red",
            },
        )

        logger = logging.getLogger("example")
        handler = logging.StreamHandler()
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.setLevel(logging.DEBUG)

        return logger

    def debug(self, str):
        self.logger.debug(str)

    def info(self, str):
        self.logger.info(str)

    def error(self, str):
        self.logger.error(str)

    def warning(self, str):
        self.logger.warning(str)

    def critical(self, str):
        self.logger.critical(str)

    @staticmethod
    def create_dir(args, time, scriptname):
        dir_path = Path.cwd()
        save_path = "{}/results/{}-({})".format(dir_path, time, scriptname)
        # create model dir
        os.makedirs("{}/checkpoints".format(save_path))
        # create code dir
        os.makedirs("{}/src".format(save_path))
        # create summary dir
        os.makedirs("{}/summary".format(save_path))

        dest = "{}/src".format(save_path)
        # copy files
        filelist = os.listdir(dir_path)
        for filename in filelist:
            if os.path.isdir("{}/{}".format(dir_path, filename)):
                extend_dir = "{}/{}".format(dir_path, filename)
                os.makedirs("{}/{}/".format(dest, filename))
                for subfilename in os.listdir(extend_dir):
                    if subfilename.endswith(".py") or subfilename.endswith(".sh"):
                        shutil.copyfile("{}/{}".format(extend_dir, subfilename),
                                        "{}/{}/{}".format(dest, filename, subfilename))

            else:
                if filename.endswith(".py") or filename.endswith(".sh"):
                    shutil.copyfile("{}/{}".format(dir_path, filename), "{}/{}".format(dest, filename))

        # Add log file
        logfile_path = "{}/log-{}.log".format(save_path, time)
        logger.remove()
        logger.add(logfile_path, level=args.log_level)

        # Print Configuration
        sorted_list = sorted(vars(args).items(), key=operator.itemgetter(0))
        logger.info("#" * 120)
        logger.info("----------Configurable Parameters In this Model----------")
        for name, val in sorted_list:
            logger.info("# " + ("%20s" % name) + ":\t" + str(get_list_name(val)))
        logger.info("#" * 120)

        return logfile_path
